script_path=string.sub(arg[0],1,-14)
package.path=script_path.."/?.lua;"..package.path
require('mylib')
source_file_name,source_path=os.processFileName(arg[1])
if source_path=='' then source_path='.' end

do -- user configurations : all these can be changed in input file or from command line argument.
	verbosecpp=true  -- easy for debugging cpp compilation error
	verbose=false
	gen_lua={}
	gen_lua.supportedClassProperties={'^ifdef$','^ifndef$','^isLuaInheritable$','^isExtendableFromLua$',
	'^decl$','^if_$', '^enums$','^inheritsFrom$', '^name$','^className$','^ctors$','^wrapperCode$',
	'^globalWrapperCode',  '^staticMemberFunctions$', '^memberFunctionsFromFile$', '^memberFunctions$',
	'^read_properties$','^write_properties$','^customFunctionsToRegister$', '^properties$',}
	gen_lua.number_types={'int', 'double', 'float','long'}
	gen_lua.enum_types={}
	gen_lua.boolean_types={'bool'}
	gen_lua.string_types={'const%s+char%s*%*', 'std%s*::%s*string', 'TString'} -- string types has to support static conversion  to const char*, and construction from const char*.
	gen_lua.string_to_cstr={'@'              , '@.c_str()',         '@.ptr()'}
	gen_lua.string_from_cstr={'@'            , 'std::string(@)',    'TString(@)'}
	gen_lua.auto_rename={{'__eq','operator%s*=='},{'__call', 'operator%s*%[%s*%]'},{'__le', 'operator%s*<='},{'__lt', 'operator%s*<'},{'rsub', 'operator%s*-='},{'rmod', 'operator%s*%%='},{'radd', 'operator%s*+='},{'rmult', 'operator%s*%*='}, {'rdiv', 'operator%s*/='},{'assign', 'operator%s*='},{'__call','operator%s*%(%s*%)'},{'__div','operator%s*/'},{'__mul','operator%s*%*'},{'__add','operator%s*+'}, {'__sub','operator%s*-'},{'__mod', 'operator%s*%%'},} -- operator()
	gen_lua.modifiers={'const', '%*', '&','inline','virtual'}
	gen_lua.single_letter_modifiers={'%*', '&'}
	gen_lua.enable_type_checking=true  -- even with this option turned off, some type checking that don't affect computation time is still turned on.
	gen_lua.generate_debug_printf=false -- for debugging luna_gen.lua
	gen_lua.catch_cpp_exception=true  -- catches std::exception(...)
	gen_lua.no_unmodified_overwriting=false  -- set true if you don't want to overwrite the output file when it's unchanged. Setting this true can conflict with make's dependency checking. 
	gen_lua.use_profiler=false -- uses performance profiler. See test_profiler sample.
end

-- How to read code.
-- Parsing: 
-- function buildDefinitionDB(...)

-- Main: at the end of this file


do -- system conf : will be generated by the system
	gen_lua.type_error_msg='type "gen_lua.type_names" to see all registered types'
	gen_lua.cpp_contents={}
	gen_lua.complex_type_names={} -- in pattern form
	gen_lua.luna_types={} -- will be obtained from build definitionsDB. 
	gen_lua.type_names={'void'} -- in normalized form (see normalizeClassName below)
	gen_lua.usedHashValue={}
	gen_lua.getNameSpace={}
	gen_lua.defNameSpaceCode=''
	gen_lua.definitionDBbuilt=false
	gen_lua.declarationWritten=false
	gen_lua.writeHeaderCounter=1
	gen_lua.moduleUniqueId=1
end

-- TODO:
-- overloading doesn't work when there are both staticMemberFunctions and memberFunctions having the same name.
-- 
-- NOTE:
-- Please use editors that support source-code folding based on indentation. (In my case, vim).
-- Otherwise, this code would be very difficult to read.

-- default options
do -- utility functions
	function processNamespaces(parent, parentName)

		local str=""
		if parent then
			for k, v in pairs(parent) do
				if type(v)=='table' then
					if parentName~="" then
						str=str..'\n'.. [[
						if ]]..parentName..k..[[==nil then
							]]..parentName..k..[[={}
						end
						]]
					end
				end
			end
			for k, v in pairs(parent) do
				if type(v)=='table' then
					for i,cname in ipairs(v) do
						gen_lua.getNameSpace[normalizeClassName(cname)]=parentName..k
					end
				end
			end
			for k, v in pairs(parent) do
				if type(v)=='table' then
					str=str..'\n'..processNamespaces(v, parentName..k..'.')
				end
			end
		end
		return str
	end
	function luacodeInCquote(luacode)
		luacode=string.gsub(luacode, "\n","\\n")
		luacode=string.gsub(luacode, "\"","\\\"")
		return '"'..luacode..'"'
	end
	function filter(check, valtrue, valfalse)
		if check then return valtrue end
		return valfalse
	end
	function codeDostring(luaCode)
		return "	luna_dostring(L,"..luacodeInCquote(luaCode)..");" -- luaL_dostring followed by pcall error checking 
	end
	function codeRequireMylib(additionalLuaCode)
		return "luna_dostring(L, "..luacodeInCquote([[
		package.path="]]..script_path..[[/?.lua;package.path"
		require('mylib')
		]]..(additionalLuaCode or "") ) -- closing luacodeInCquote 
		..");\n" -- closing luaL_dostring
	end
	function writeLaunchLuaDebugger()
		write(codeRequireMylib('dbg.console()'))
	end
	
	function loadDefinitionDB(filename)
		local db=loadstring('return '..util.readFile(filename))()
		array.concat(gen_lua.cpp_contents, db.cpp_contents)
		array.concat(gen_lua.complex_type_names, db.complex_type_names)
		array.concat(gen_lua.type_names, db.type_names)
		table.mergeInPlace(gen_lua.luna_types, db.luna_types)
		table.mergeInPlace(gen_lua.usedHashValue, db.usedHashValue)
		array.concat(gen_lua.getNameSpace, db.getNameSpace)
		--array.concat(gen_lua.getNameSpaceCode, db.getNameSpaceCode)
		gen_lua.moduleUniqueId=gen_lua.moduleUniqueId+db.moduleUniqueId+1
		gen_lua.writeHeaderCounter=gen_lua.writeHeaderCounter+db.writeHeaderCounter+1
		for i,v in ipairs(db.number_types) do 
			array.pushBackIfNotExist(gen_lua.number_types,v)
		end
		for i,v in ipairs(db.enum_types) do 
			array.pushBackIfNotExist(gen_lua.enum_types,v)
		end
		for i,v in ipairs(db.boolean_types) do 
			array.pushBackIfNotExist(gen_lua.boolean_types,v)
		end
	end
	function writeDefinitionDBtoFile(filename)
		local db={}
		db.cpp_contents=gen_lua.cpp_contents
		db.complex_type_names=gen_lua.complex_type_names
		db.luna_types={}
		-- copy properties that are absolutely necessary
		for k,v in pairs(gen_lua.luna_types) do
			local tbl={}
			tbl.external=true
			tbl.className=v.className
			tbl.isModule=v.isModule
			tbl.uniqueID=v.uniqueID
			tbl.uniqueLuaClassname=v.uniqueLuaClassname
			tbl.uppermostParent={
				className=v.uppermostParent.className,
				uniqueID=v.uppermostParent.uniqueID,
			}
			db.luna_types[k]=tbl
		end
		db.type_names=gen_lua.type_names
		db.usedHashValue=gen_lua.usedHashValue
		db.getNameSpace=gen_lua.getNameSpace
		db.getNameSpaceCode=gen_lua.getNameSpaceCode
		db.writeHeaderCounter= gen_lua.writeHeaderCounter
		db.moduleUniqueId= gen_lua.moduleUniqueId
		db.number_types=gen_lua.number_types
		db.enum_types=gen_lua.enum_types
		db.boolean_types=gen_lua.boolean_types
		local str=table.tostring(db)
		print('generating '..filename)
		util.writeFile(filename, str)
	end

	function writeIncludeBlock(not_include_luna)
		write([[
		#include <stdio.h>
		#include <string.h>
		#include <string>
		extern "C"
		{
			#include "lua.h"
			#include "lualib.h"
			#include "lauxlib.h"
			//#include "luadebug.h"
		}
		]])
		if not not_include_luna then
			write('#include "luna.h"')
		end
		if gen_lua.use_profiler then
			write([[
			#include "QPerformanceTimer.h"
			]])
		end
	end
	function addOverloadDefinition(retType, defaultRetVal, overloaded)
		-- overloaded function body definition: redirect function calls to the relevant overloaded functions
		for k,v in pairs(overloaded) do
			local vv=v[1]
			addLine('  static '..retType..' _bind_'..k..'(lua_State *L)\n  {')

			for i,vv in ipairs(v) do
				addLine('    if (_lg_typecheck_'..vv.luaname..'(L)) return _bind_'..vv.luaname..'(L);')
			end
			local msg='\\n'
			for i,vv in ipairs(v) do
				msg=msg..'('..argsToString(vv.args)..')\\n'
			end

			addLine('    luaL_error(L, "'..k..' ( cannot find overloads:)'..msg..'");\n')
			addLine('    return '..defaultRetVal..';')
			addLine('  }')
		end
	end
	function tokenizeArg(arg, tokens) -- input: const char* a -> output: const, char, *, a
		local ttokens= string.tokenize(arg, '%s')
		
		for j,m in ipairs(tokens) do
			local tokens={}
			for i,v in ipairs(ttokens) do
				array.concat(tokens, string.tokenize2(v, m, string.gsub(m,'%%','')))
			end
			ttokens=tokens
		end
		
		return ttokens
	end
	function genInterfaceName(luaclass)
		if not luaclass.isModule then
			return 'LunaTraits<'..normalizedClassNameToClassName(luaclass.className)..' >'
		else
			return 'luna__interface_'..string.gsub(luaclass.uniqueLuaClassname,'@','_1_')
		end
	end
	function Hash(str)
		local n=#str
		local h=0
		for i=1,n do
			h=31*h+string.byte(str,i)
			h=math.mod(h,100000000+i)
		end
		return h
	end

	function lgerror(...)
		print("Error!",...)
		print('\nCallstack:')
		dbg.callstack()
		print('\nEntering debugging console (typically typing c, c2, c3, c4, ... reveals more details)')
		dbg.console()
	end
	function addReturnType(vv, cppname)
		if vv.if_ then
			addLine('#if '..vv.if_)
		end 
		
		local catch=gen_lua.catch_cpp_exception
		if vv.returnType.c=='void' then
			if catch then addLine('\ttry {') end
			addLine('\t'..cppname..'('..parsedNames(vv.args)..');')
			if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
			addLine('	return 0;')
		else
			if isLunaType(vv.returnType) then
				local ia=vv.returnType
				local cp=findLunaClassProperty(ia)
				local cppclass_name=normalizedClassNameToClassName(cp.className)
				local cpp_parent_classname=normalizedClassNameToClassName(cp.uppermostParent.className)
				local cppinterface_name=genInterfaceName(cp)
				local t=ia.t
				local ii=string.find(ia.t,'%*')
				if ii then
					-- pointer type:
					local adopt=vv.adopt or false
					if catch then addLine('\ttry {') end
					addLine('\t'..vv.returnType.t..' ret='..cppname..'('..parsedNames(vv.args)..');')
					addLine('   if (ret==NULL) lua_pushnil(L); else ')
					addLine('	Luna<'..cpp_parent_classname..' >::push(L,('..cpp_parent_classname..' *)ret,'..tostring(adopt)..',"'..cp.uniqueLuaClassname..'");')
					if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
				else
					ii=string.find(ia.t,'%&') 
					if ii then
						local adopt=false
						-- reference type: 
						if catch then addLine('\ttry {') end
						addLine('\t'..vv.returnType.t..' ret='..cppname..'('..parsedNames(vv.args)..');')
						addLine('	Luna<'..cpp_parent_classname..' >::push(L,&ret,'..tostring(adopt)..',"'..cp.uniqueLuaClassname..'");')
						if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
					else
						local adopt=true
						-- value type : copy constructor
						if catch then addLine('\ttry {') end
						local inputToCtor=cppname..'('..parsedNames(vv.args)..')'
						if inputToCtor=='self.operator-()' then
							inputToCtor='-self'
						end
						addLine('\t'..vv.returnType.t..'* ret=new '..vv.returnType.t..'('..inputToCtor..');')
						addLine('	Luna<'..cpp_parent_classname..' >::push(L,ret,'..tostring(adopt)..',"'..cp.uniqueLuaClassname..'");')
						if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
					end
				end
				addLine('	return 1;')
			elseif isNumberType(vv.returnType) then
				if catch then addLine('\ttry {') end
				addLine('\t'..vv.returnType.t..' ret='..cppname..'('..parsedNames(vv.args)..');')
				addLine('	lua_pushnumber(L, ret);')
				if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
				addLine('	return 1;')
			elseif isStringType(vv.returnType) then
				if catch then addLine('\ttry {') end
				addLine('\t'..vv.returnType.t..' ret='..cppname..'('..parsedNames(vv.args)..');')
				for i,v in ipairs(gen_lua.string_types) do
					if select(1,string.find(vv.returnType.c, v))~=nil then
						addLine('	lua_pushstring(L, '..string.gsub(gen_lua.string_to_cstr[i],'@','ret')..');')
						break
					end
				end
				if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
				addLine('	return 1;')

			elseif isBooleanType(vv.returnType) then
				if catch then addLine('\ttry {') end
				addLine('\t'..vv.returnType.t..' ret='..cppname..'('..parsedNames(vv.args)..');')
				addLine('	lua_pushboolean(L, ret);')
				if catch then addLine('\t} catch(std::exception& e) { luaL_error( L,e.what()); }') end
				addLine('	return 1;')
			elseif vv.returnType.t=='void' then
				addLine('	return 0;')
			else
				lgerror('unknown return type '..vv.returnType.t..':'..table.tostring(vv))
			end
		end
		if vv.if_ then
			addLine('#else')
			addLine('   return 0;')
			addLine('#endif')
		end 
		addLine('  }')
	end
	function addLine(line)
		if verbose==true then
			print(':: '..line)
		end
		if verbosecpp then
			local linen = debug.getinfo(2).currentline
			line=string.gsub(line, '\t', '    ')
			local lines=string.lines(line)
			local lline=lines[#lines]
			if #lline<61 then
				lines[#lines]=lline..string.rep(' ',61-#lline) 
			end
			line=table.concat(lines, '\n')
			array.pushBack(gen_lua.cpp_contents, line.." // "..linen)
		else
			array.pushBack(gen_lua.cpp_contents, line)
		end
	end
	function isLunaType(ia) 
		if ia.t==nil then lgerror('Error! isLunaType') end
		return findLunaClassProperty(ia)~=nil
	end
	function findLunaClassProperty(ia)
		local str=ia.c
		if( not str) then lgerror('findLunaClassProperty', ia.t, ia.n, ia.c) end
		return gen_lua.luna_types[normalizeClassName(str)]
	end
	function isNumberType(ia) 
		if ia.t==nil then lgerror('Error! isNumberType') end
		if string.isMatched(ia.t, gen_lua.number_types) then
			return true
		end
		if string.isMatched(ia.t, gen_lua.enum_types) then
			return true
		end
		return false
	end
	function isEnumType(ia)
		if ia.t==nil then lgerror('Error! isNumberType') end
		if string.isMatched(ia.t, gen_lua.enum_types) then
			return true
		end
		return false
	end
	function isBooleanType(ia) 
		if ia.t==nil then lgerror('Error! isBooleanType') end
		if string.isMatched(ia.t, gen_lua.boolean_types) then
			return true
		end
		return false
	end
	function isStringType(ia) 
		if ia.t==nil then lgerror('Error! isStringType') end
		if string.isMatched(ia.t, gen_lua.string_types) then
			return true
		end
		return false
	end
	function parsedNames(arg)
		local str=''
		for i,v in ipairs(arg) do
			str=str..', '..v.n
		end
		return string.sub(str,2)
	end
	function tagOverloadedFunctions(ftns)
		local nameTable={}
		for i, ftn in ipairs(ftns) do
			if nameTable[ftn.luaname]==nil then
				nameTable[ftn.luaname]={ftn}
			else
				array.pushBack(nameTable[ftn.luaname], ftn)
			end
		end
		for k, nt in pairs(nameTable) do
			if #nt>1 then
				local orig_luaname
				for i,ftn in ipairs(nt) do
					ftn.overloaded=true
					orig_luaname=ftn.luaname
					ftn.luaname=ftn.luaname..'_overload_'..tostring(i)
				end
				if ftns.overloaded==nil then
					ftns.overloaded={}
				end
				ftns.overloaded[orig_luaname]= nt
			end
		end
	end
	function parseMemberFunction (fn)
		local tbl
		if type(fn)=='table' then
			tbl=parseMemberFunction(fn[1])
			for k,v in pairs(fn) do
				if fn.rename then
					tbl.luaname=fn.rename
				elseif k~=1 then
					tbl[k]=v -- pass through other options
				end
			end
			return tbl
		else
			assert(type(fn)=='string')

			local cppname_orig
			for i,vv in ipairs(gen_lua.auto_rename) do
				local k=vv[1]
				local v=vv[2]
				local s,e=string.find(fn, v)
				if s then
					cppname_orig=string.sub(fn, s,e)
					fn=string.gsub(fn,v,k)
				end
			end
			if select(1,string.find(fn,'=')) then
				lgerror(fn..'\ndefault parameter is not allowed. Declare two functions instead. E.g. (X) void a(int a=3)  \n(O) void a(int a)\n    void a()  ')
			end
			local s,e,c=string.find(fn, '%(')
			if not s then lgerror('unknown function definition format', fn)  end
			local part1=string.sub(fn,1,s-1)
			local s2,e2=string.find(fn, '%)')
			if not s2 or s2<s then lgerror('unknown function definition format', fn)  end

			local part2=string.sub(fn, e,s2)
			
			part1=string.trimRight(part1)
			local a,b=os.rightTokenize(part1, '[%s&%*]')

			local ccc=string.find(b,'::')
			local luaname=b
			if ccc then
				luaname=string.sub(b, ccc+2) -- discard cpp namespace name
			end
			local input_args=parseArg(part2)
			if #input_args==1 and input_args[1].t=='void' then
				input_args[1]=nil
			end
			tbl={returnType= parseArg("("..string.trimSpaces(a)..")")[1], cppname=cppname_orig or b, luaname=luaname, args=input_args}
		end
		return tbl
	end
	function argsToString(args)
		local str=""
		for i=1,#args do
			local arg=args[i]
			str=str..arg.t..' '..arg.n..','
		end
		return str
	end

	function addParsedArg(vv, args)
		if gen_lua.enable_type_checking and not vv.overloaded then
			addLine('    if (!_lg_typecheck_'..vv.luaname..'(L)) { luna_printStack(L); luaL_error(L, "luna typecheck failed:'..vv.luaname..'('..argsToString(args)..')"); }\n')
		end
		for i,ia in ipairs(args) do
			if isLunaType(ia) then
				--local t=string.gsub(ia.t,'%&','') -- remove reference
				local t=ia.t

				local cp=findLunaClassProperty(ia)
				local cppclass_name=normalizedClassNameToClassName(cp.className)
				local cppinterface_name=genInterfaceName(cp)
				local cpp_parent_classname=normalizedClassNameToClassName(cp.uppermostParent.className)
				local t_modifiers=string.gsub(t, normalizedClassNameToPattern(normalizeClassName(ia.c)), '')
				if select(1,string.find(t_modifiers,'*')) then
					addLine('	'..t..' '..ia.n..'=static_cast<'..cppclass_name..' *>(Luna<'..cpp_parent_classname..' >::check(L,'..i..'));')
				else
					local ii=string.find(ia.t,'%&') 
					if not ii then t=t..'&' end -- add reference
					addLine('	'..t..' '..ia.n..'=static_cast<'..cppclass_name..' &>(*Luna<'..cpp_parent_classname..' >::check(L,'..i..'));')
				end
			elseif isStringType(ia) then
				addLine('	'..ia.t..' '..ia.n..'=('..ia.t..')lua_tostring(L,'..i..');')
			elseif isNumberType(ia) then
				if isEnumType(ia) then
					addLine('	'..ia.t..' '..ia.n..'=('..ia.t..')(int)lua_tonumber(L,'..i..');')
				else
					addLine('	'..ia.t..' '..ia.n..'=('..ia.t..')lua_tonumber(L,'..i..');')
				end	
			elseif isBooleanType(ia) then
				addLine('	'..ia.t..' '..ia.n..'=('..ia.t..')lua_toboolean(L,'..i..');')
			else
				lgerror('Unknown or unsupported argument type: ',table.tostring(ia))
			end
		end
	end
	function addTypeCheck(args)

		local debug_printf=gen_lua.generate_debug_printf
		if debug_printf then
			addLine('printf("luaget_top:%d\\n",lua_gettop(L));')
		end
		addLine('	if( lua_gettop(L)!='..#args..') return false;')
		for i,ia in ipairs(args) do
			if isLunaType(ia) then
				local luaclass=findLunaClassProperty(ia)
				if debug_printf then
					addLine('printf("unique_id:%d=='..luaclass.uniqueID..'\\n",luna_t::get_uniqueid(L,'..i..'));')
				end
				addLine('	if( Luna<void>::get_uniqueid(L,'..i..')!='..luaclass.uppermostParent.uniqueID..') return false; // '..normalizedClassNameToClassName(luaclass.uppermostParent.className))
			elseif isNumberType(ia) then
				addLine('	if( lua_isnumber(L,'..i..')==0) return false;')
				if debug_printf then
					addLine('printf("lua_isnumber:%d\\n",lua_isnumber(L,'..i..'));')
				end
			elseif isStringType(ia) then
				addLine('	if( lua_isstring(L,'..i..')==0) return false;')
				if debug_printf then
					addLine('printf("lua_isstring:%d\\n",lua_isstring(L,'..i..'));')
				end
			elseif isBooleanType(ia) then
				addLine('	if( lua_isboolean(L,'..i..')==0) return false;')
				if debug_printf then
					addLine('printf("lua_isboolean:%d\\n",lua_isstring(L,'..i..'));')
				end
			else
				lgerror('Unknown or unsupported argument type: ',table.tostring(ia))
			end
		end
		addLine('	return true;\n  }')
	end
	function write(ctn)
		local lines=string.lines(ctn)

		if verbose then 
			for i,l in ipairs(lines) do
				print ("::"..ctn)
			end
		end
		array.pushBack(gen_lua.cpp_contents, unpack(lines))
	end
	-- normalizeClassName using theree special character ` (means %s+) and ~ (means %s*) and @p (means * - pointer)
	-- const char*, const   char  *, const\tchar\t*, and so on becomes 'const`char`@p'
	-- std::string, std::vector<std::string>, and so on becomes
	-- std~::~string, std~::~vector~<~std~::~string~>
	-- the goal is that normalizeClassName should not be tokenized when using tokenizeArg function
	gen_lua.normC={}
	gen_lua.normC.token_sep={'::','<','>','%*',','} -- every tokens that can appear in a class type name
	gen_lua.normC.token_conv={'@o','@s','@l', '@p', '@c'}
	gen_lua.normC.token_pattern={'::','<','>','%%%*',','}
	function patternToNormalizedClassName(classNamePattern)
		local str=string.gsub(classNamePattern,'%%s%+','`')
		str=string.gsub(str,'%%s%*','~')
		local normC=gen_lua.normC
		for i=1,#normC.token_sep do
			str=string.gsub(str, normC.token_pattern[i], normC.token_conv[i])
		end
		return str
	end
	function normalizedClassNameToPattern(normalizedClassName)
		local str=string.gsub(normalizedClassName,'`','%%s+')
		str=string.gsub(str,'~','%%s*')
		local normC=gen_lua.normC
		for i=1,#normC.token_sep do
			str=string.gsub(str, normC.token_conv[i], normC.token_pattern[i])
		end
		return str
	end
	function normalizedClassNameToClassName(normalizedClassName)
		if normalizedClassName==nil then lgerror('normalizedClassName = nil') end
		local str=string.gsub(normalizedClassName,'`',' ')
		str=string.gsub(str,'~','')
		local normC=gen_lua.normC
		for i=1,#normC.token_sep do
			str=string.gsub(str, normC.token_conv[i], ' '..normC.token_sep[i])
		end
		return str
	end
	function normalizeClassName(classNameOrPattern)
		local str=classNameOrPattern
		
		if select(1,string.find(str,'%%')) then
			str=patternToNormalizedClassName(str)
		end
		local token_sep=gen_lua.normC.token_sep
		local token_conv=gen_lua.normC.token_conv

		local tokens=tokenizeArg(str, token_sep)
		local sm={}
		for i=1, #tokens do 
			for j=1, #token_sep do
				if select(1,string.find(tokens[i],token_sep[j]))~=nil then
					sm[i]=true
					tokens[i]=token_conv[j]
					break
				end
			end
		end
		local out=""
		for i=1, #tokens-1 do 
			if sm[i] or sm[i+1] then
				out=out..tokens[i].."~"
			else
				out=out..tokens[i].."`"
			end
		end
		out=out..tokens[#tokens]
		return out 
	end
	-- e.g. str: (int x, double d)
	-- return {{ t='int', n='x'}, {t='double', n='d'}}
	function parseArg(str)
		str=string.gsub(str,'static%s+','')
		str=string.gsub(str, '%*', '* ') -- insert delimiting space
		str=string.gsub(str, '>', '> ')
		str=string.trimLeft(str)
		str=string.trimRight(str)
		if string.sub(str,-1)==';' then str=string.sub(str, 1,-2) end
		if (string.sub(str,1,1)~='(' or string.sub(str,-1)~=')') then
			lgerror('ctor', str, 'in unacceptable format. (ctor definition should start by ( and finish by ))')
		end
		for ictn,ctn in ipairs(gen_lua.complex_type_names) do
			local newstr=string.gsub(str, ctn, patternToNormalizedClassName(ctn))
			if verbose and str~=newstr then print('parseArg1', str, newstr) end
			str=newstr
		end

		local args=string.tokenize(string.sub(str, 2, -2),',')

		function isNonEmptyString(str)
			local s=string.find(str,'^%s*$')
			if s then
				return false
			end
			return true
		end
		local args=array.filter(isNonEmptyString,args)
		for iarg, arg in ipairs(args) do
			local tokens=array.filter(isNonEmptyString, tokenizeArg(args[iarg],gen_lua.single_letter_modifiers))
			local name=nil
			local gmatched=nil
			local typenames=gen_lua.type_names
			for i=1,#tokens do
				tokens[i]=string.trimSpaces(tokens[i])
				-- print(tokens[i])
				local matched=string.isLongestMatched(tokens[i],typenames, "[%(,]")
				if not matched
					and not string.isLongestMatched(tokens[i], gen_lua.modifiers) 
					and gen_lua.luna_types[tokens[i]]==nil
					then
					if i==#tokens then
						name=tokens[i]
					else
						lgerror('Error unknown type name : '..normalizedClassNameToClassName(args[iarg])..'\n'..gen_lua.type_error_msg)
					end
				end
				if matched then 
					
					if gmatched==nil or ( #typenames[gmatched]<#typenames[matched]) then
						-- prefer longer match
						gmatched=matched 
					end
				end
			end
			if gmatched==nil then lgerror('gmatched==nil :',arg) end
			local ntc=normalizedClassNameToClassName
			if name then
				args[iarg]={ c=ntc(gen_lua.type_names[gmatched]), t=table.concat(array.map(ntc,tokens),' ', 1,#tokens-1), n=name}
			else
				args[iarg]={ c=ntc(gen_lua.type_names[gmatched]), t=table.concat(array.map(ntc,tokens),' '), n='_arg'..iarg}
			end
			if true then -- error checking
				local aa=args[iarg]
				local v_pattern=normalizedClassNameToPattern(aa.c)
				local nn=string.gsub(aa.t, v_pattern, '')
				local mod=gen_lua.modifiers
				for md=1,#mod do
					nn=string.gsub(nn, mod[md],'')
				end
				nn=string.gsub(nn, '%s+','')
				if nn~='' then
					lgerror('undefined type "'..aa.t..'" detected while parsing "'..str..'"!\n (did you mean '..aa.c..'? what is '..nn..'?) \n '..gen_lua.type_error_msg)
				end
			end
		end
		--print(table.tostring(args))
		return args
	end

	function rectifyFunctions(functions)
		local output={}
		if type(functions)=='string' then
			--lgerror('string "'..functions..'" is given where table (of function defs) is expected')
			functions={functions}
		end
		for i,v in ipairs(functions) do
			if type(v)=="string" then
				local lines=string.lines(v)
				for il,l in ipairs(lines) do
					local s=string.find(l,'//') 
					if s then l=string.sub(l, 1, s-1)  end -- remove comments
					local s=string.find(l,'@')
					if s then -- parse options
						local ren=string.trimSpaces(string.sub(l, s+1))
						local options={}
						while true do
							local s,e,cc1,cc2=string.find(ren,'%;([^=]+)=([^%;]+)%;')
							if s==nil then break end
							options[cc1]=cc2
							ren=string.trimSpaces(string.gsub(ren,'%;[^=]+=[^%;]+%;',''))
						end
						if #ren~=0 then
							options.rename=ren
						end

						l=string.sub(l, 1, s-1)
						options[1]=l
						array.pushBack(output, options)
					else
						local s=string.find(l,'^%s*$') -- discard empty
						if not s then
							if select(1,string.find(l,'/%*')) then
								lgerror('block comment is not allowed:'.. l)
							end
							array.pushBack(output,l)
						end
					end
				end
			else
				array.pushBack(output,v)
			end
		end
		return output
	end

	function flushWritten(filename)
		local ctx_new=table.concat(gen_lua.cpp_contents, '\n')
		gen_lua.cpp_contents={}
		
		if gen_lua.no_unmodified_overwriting then
			if os.isFileExist(filename) then
				local ctx_old=util.readFile(filename)
				if ctx_old==ctx_new then
					print('luna_gen won\'t overwrite "'..os.filename(filename)..'" because no change has made.')
					return
				end
			end
		end
		print('generating '..filename)
		util.writeFile(filename, ctx_new)
			
	end
end

-- rectify function definitions 
-- input: void asdf(int narg, std::string param)
-- output: {returnType= 'void', cppname='asdf', luaname='asdf', args={{t='int', n='nArg'},{t='std::string', n='param'}}}
-- note that this modifies bindTarget table significantly!
function buildDefinitionDB(...)
	local targets={...}
	if namespaces==nil then namespaces={} end
	for i,bindTarget in ipairs(targets) do
		if bindTarget.modules then
			for k,v in ipairs(bindTarget.modules) do

				if not v.namespace then
					lgerror('module '..k..' does not have namespace')
				end
				v.namespace,v.name=string.rightTokenize(v.namespace, '%.')
				if v.name=='_G' then 
					v.namespace='_G'
				end
				v.staticMemberFunctions=v.functions
				v.isModule=true
			end
			if bindTarget.classes==nil then
				print('bindTarget.classes==nil so making an empty one')
				bindTarget.classes={}
			end
			if bindTarget.modules==nil then
				print('bindTarget.modules==nil so making an empty one')
				bindTarget.modules={}
			end
			array.concat(bindTarget.classes, bindTarget.modules) -- use the same code
		end
		if bindTarget.namespaces then
		 	namespaces=table.merge(namespaces, bindTarget.namespaces)
		end
	end
	local bindTarget
	if #targets==1 then
		bindTarget=targets[1]
	else
		bindTarget={}
		bindTarget.modules={}
		bindTarget.classes={}
		for i,t in ipairs(targets) do -- shallow copy
			array.pushBack(bindTarget.modules, unpack(t.modules))
			array.pushBack(bindTarget.classes,unpack(t.classes))
		end
	end
	assert(gen_lua.definitionDBbuilt==false)
	for i=1,#gen_lua.number_types do
		--print(gen_lua.number_types[i])
		gen_lua.number_types[i]=normalizedClassNameToPattern(normalizeClassName(gen_lua.number_types[i]))
	end
	for i=1,#gen_lua.enum_types do
		gen_lua.enum_types[i]=normalizedClassNameToPattern(normalizeClassName(gen_lua.enum_types[i]))
	end

	array.concat(gen_lua.type_names, array.map(normalizeClassName, gen_lua.number_types))
	array.concat(gen_lua.type_names, array.map(normalizeClassName, gen_lua.enum_types))
	array.concat(gen_lua.type_names, array.map(normalizeClassName, gen_lua.string_types))
	array.concat(gen_lua.type_names, array.map(normalizeClassName, gen_lua.boolean_types))
	gen_lua.defNameSpaceCode=processNamespaces(namespaces, '_G.')
	-- 1. collect class names
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.ifdef then
			luaclass.if_='defined ('..luaclass.ifdef..')'
		end
		if luaclass.ifndef then
			luaclass.if_='!defined ('..luaclass.ifndef..')'
		end
		for k,v in pairs(luaclass) do -- check syntax error of user-given definition
			if not string.isMatched(k, gen_lua.supportedClassProperties) then
				if luaclass.isModule then
					if not string.isMatched(k, {'namespace', 'isModule', 'functions'}) then
						lgerror('Syntax error: unknown property: ',k,'in',luaclass.name)
					end
				else
					if tonumber(k) then
						print('??? numbered index is not expected. ')
						printTable(v)
					end
					lgerror('Syntax error: unknown property: ',k,'in',luaclass.name)
				end
			end
		end
		luaclass.iluaclass=iluaclass
		luaclass.className=normalizeClassName(luaclass.className or 
											  string.gsub(luaclass.name, '%.', '::'))
		array.pushBack(gen_lua.type_names, luaclass.className)
		-- if namespace (say NS) is defined, 
		-- two metatables are defined:
		--  1. __luna.NS_luaclass_name
		--  2.  NS.luaclass_name
		--  where 2 is simply a reference to 1. 
		--  Note that this doens't have performance overhead but has slight memory overhead 
		--  (depending on the number of classes as opposed to the number of instances)
		
		do
			-- process luaclass name and namespace name
			local uluaclass_name=luaclass.name
			if gen_lua.getNameSpace[luaclass.className] then
				luaclass.namespace=gen_lua.getNameSpace[luaclass.className]
			end
			
			local v=luaclass
			if v.namespace==nil then
				v.namespace,v.name=string.rightTokenize(v.name, '%.')
			else
				v.namespace,v.name=string.rightTokenize(v.namespace..'.'..v.name, '%.')
			end
			uluaclass_name=string.gsub(luaclass.namespace,'%.', '_')..'_'..luaclass.name
			luaclass.uniqueLuaClassname=uluaclass_name
			luaclass.cppinterface_name=genInterfaceName(luaclass)
		end


		if not luaclass.isModule then
			luaclass.uniqueID=Hash(luaclass.uniqueLuaClassname)
			while true do -- generate unique id (though hash value is not very likely to collide.)
				luaclass.uniqueID=luaclass.uniqueID+23
				if gen_lua.usedHashValue[tostring(luaclass.uniqueID)] ==nil then
					gen_lua.usedHashValue[tostring(luaclass.uniqueID)] =luaclass.className
					break
				end
			end
		end

		if gen_lua.luna_types[luaclass.className]==nil then
			gen_lua.luna_types[luaclass.className]=luaclass
		elseif luaclass.isModule then
			luaclass.cppinterface_name=luaclass.cppinterface_name..gen_lua.moduleUniqueId
			gen_lua.moduleUniqueId=gen_lua.moduleUniqueId+1
			gen_lua.luna_types[luaclass.className]=luaclass
		else
			lgerror('duplicated definition? '..luaclass.className)
		end
	end

	-- 1.5. register complex_type_names
	for i,v in ipairs(gen_lua.type_names) do
		if verbose then print('type:'..v) end
		local v_pattern=normalizedClassNameToPattern(v)
		if v~=v_pattern then
			if verbose then print('registering complex typename',v_pattern) end
			array.pushBack(gen_lua.complex_type_names,v_pattern)
		end
	end
	table.sort(gen_lua.complex_type_names, function(a,b) return #a>#b end)

	if verbose then
		print('sorted regitered complex types:')
		for i,v in ipairs(gen_lua.complex_type_names) do print(v.." ") end
	end

	-- 1.6 register property getter and setters
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.properties then
			-- auto generate read_properties and write_properties functions
			
			if not luaclass.wrapperCode then luaclass.wrapperCode='' end
			if not luaclass.read_properties then luaclass.read_properties= {} end
			if not luaclass.write_properties then luaclass.write_properties={} end
			if not luaclass.staticMemberFunctions then luaclass.staticMemberFunctions={} end
			if not luaclass.memberFunctions then luaclass.memberFunctions={} end

			local propertyLines={}
			for ip, p in ipairs(luaclass.properties) do
				local cc=string.lines(p)
				for i=1,#cc do
					local s=string.find(cc[i],'//') 
					if s then cc[i]=string.sub(cc[i], 1, s-1)  end -- remove comments
					cc[i]=string.trimSpaces(cc[i])
					if string.len(cc[i])>1 then
						array.pushBack(propertyLines, cc[i])
					end
				end
			end

			for ip, p in ipairs(propertyLines) do
				
				local s=string.find(p,'@')
				local luaname
				if s then -- parse options
					luaname=string.trimSpaces(string.sub(p,s+1))
					p=string.sub(p, 1, s-1)
				end
				p=string.gsub(p,';','')
				local cp=parseArg('('..p..')')[1]
				if (not cp) then lgerror('cannot parse property ', p)  end
				array.pushBack(luaclass.read_properties,{luaname or cp.n,'_property_get_'..cp.n})
				if isLunaType(cp) then
					local cppclass_name=normalizedClassNameToClassName(luaclass.className)
					local dc='static '..cp.t..'& _property_get_'..cp.n..'('..cppclass_name..' const& a)'
					local wc='inline '..dc..' { return ('..cp.t..' &) a.'..cp.n..'; }'
					luaclass.wrapperCode=luaclass.wrapperCode..wc..'\n'
					array.pushBack(luaclass.staticMemberFunctions,dc..'\n')
				elseif isStringType(cp) then
					array.pushBack(luaclass.write_properties,{luaname or cp.n,'_property_set_'..cp.n})
					local cppclass_name=normalizedClassNameToClassName(luaclass.className)
					local dc='static '..cp.t..' _property_get_'..cp.n..'('..cppclass_name..' const& a)'
					local wc='inline '..dc..' { return a.'..cp.n..'; }'
					local dc2='static void _property_set_'..cp.n..'('..cppclass_name..' & a, '..cp.t..' b)'
					local wc2='inline '..dc2..'{ a.'..cp.n..'=b;}'
					luaclass.wrapperCode=luaclass.wrapperCode..wc..wc2..'\n'
					array.pushBack(luaclass.staticMemberFunctions,dc..'\n'..dc2..'\n')
				else
					array.pushBack(luaclass.write_properties,{luaname or cp.n,'_property_set_'..cp.n})
					local cppclass_name=normalizedClassNameToClassName(luaclass.className)
					local dc='static '..cp.t..' _property_get_'..cp.n..'('..cppclass_name..' const& a)'
					local wc='inline '..dc..' { return a.'..cp.n..'; }'
					local dc2='static void _property_set_'..cp.n..'('..cppclass_name..' & a, '..cp.t..' b)'
					local wc2='inline '..dc2..'{ a.'..cp.n..'=b;}'
					luaclass.wrapperCode=luaclass.wrapperCode..wc..wc2..'\n'
					array.pushBack(luaclass.staticMemberFunctions,dc..'\n'..dc2..'\n')
				end
			end
		end
		if luaclass.read_properties or luaclass.write_properties then
			luaclass.defineIndexMetaMethods=true
		end
	end

	-- 1.7. register parent class. (read_properties and write_properties also need to be copied)
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		luaclass.uppermostParent=luaclass
		local defineIndexMetaMethods=luaclass.defineIndexMetaMethods
		while luaclass.uppermostParent.inheritsFrom do
			luaclass.uppermostParent=findLunaClassProperty({c=luaclass.uppermostParent.inheritsFrom })
			if luaclass.uppermostParent.defineIndexMetaMethods then
				defineIndexMetaMethods=true
			end
		end
		luaclass.uniqueID=luaclass.uppermostParent.uniqueID -- share unique id for simplicity
		luaclass.defineInheritedIndexMetaMethods=defineIndexMetaMethods
	end

	-- 1.8. luainheritable?
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.isLuaInheritable then
			local cpp_parent_classname=normalizedClassNameToClassName(luaclass.uppermostParent.className)
			if luaclass.customFunctionsToRegister==nil then
				luaclass.customFunctionsToRegister={}
			end
			luaclass.isExtendableFromLua=true
			luaclass.defineInheritedIndexMetaMethods=true 
			-- TODO: need to add the metatable.__index fallback in the default __index and __newindex functions.
			array.pushBack(luaclass.customFunctionsToRegister, {'::Luna<'..cpp_parent_classname..' >::new_modified_T', 'new_modified_T'})
		end
	end

	-- 2. parse function definitions
	for iluaclass, luaclass in ipairs(bindTarget.classes) do

		if luaclass.ctors then -- parse ctor
			luaclass.ctors=rectifyFunctions(luaclass.ctors)
			for i, ctor in ipairs(luaclass.ctors) do 
				local arg=parseArg(luaclass.ctors[i])
				if #arg==1 and arg[1].t=='void' then -- void a(void)  -> void a()
					arg[1]=nil
				end
				luaclass.ctors[i]={cppname='ctor', luaname='ctor', args=arg}
			end
			tagOverloadedFunctions(luaclass.ctors)
		end
		do -- parse definitions from file
			if luaclass.memberFunctionsFromFile then
				local mfff=luaclass.memberFunctionsFromFile
				local fn=mfff[1]
				local contents=util.readFile(fn)
				if not contents then  lgerror('file not found:'..fn) end

				local cc=string.lines(contents)
				for i=1,#cc do
					local s=string.find(cc[i],'//') 
					if s then cc[i]=string.sub(cc[i], 1, s-1)  end -- remove comments
				end
				
				for i=2,#mfff do
					local funcName=mfff[i] -- {luaname, cppname}
					local declFound
					for j=1,#cc do
						local decl=cc[j]
						local s,e=string.find(decl, "%s"..funcName[2].."[%(%s]")
						if s then
							local s,e,c=string.find(decl, "({.+})")
							if s then
								declFound=string.sub(decl,1,s-1)
							else
								declFound=decl
							end
							break
						end
					end
					if declFound then
						if verbose then print(funcName[1]..":"..declFound) end
						array.pushBack(luaclass.memberFunctions, declFound.." @ "..funcName[1])
					else
						lgerror('decl not found :'..fn..':'..funcName[2])
					end
				end
			end
		end

		do -- parse members
			luaclass.memberFunctions=luaclass.memberFunctions or {}
			luaclass.memberFunctions=rectifyFunctions(luaclass.memberFunctions)

			for i,v in ipairs(luaclass.memberFunctions) do
				local vv=parseMemberFunction(v)	
				luaclass.memberFunctions[i]=vv
			end
		end
		do -- parse static members
			luaclass.staticMemberFunctions=luaclass.staticMemberFunctions or {}
			luaclass.staticMemberFunctions=rectifyFunctions(luaclass.staticMemberFunctions)

			for i,v in ipairs(luaclass.staticMemberFunctions) do
				local vv=parseMemberFunction(v)	
				luaclass.staticMemberFunctions[i]=vv
			end
		end
		-- merge members and static members
		do
			luaclass.allMemberFunctions={}
			for i,v in ipairs(luaclass.memberFunctions) do
				local vv=shallowCopyTable(v)
				local cppclass_name=normalizedClassNameToClassName(luaclass.className)
				vv.extended_args=array.concatMulti({{t=cppclass_name .."&", c=cppclass_name ,n='self'}},vv.args);
				vv.return_type='self.'..vv.cppname
				array.pushBack(luaclass.allMemberFunctions, vv)
			end
			for i,v in ipairs(luaclass.staticMemberFunctions) do
				local vv=shallowCopyTable(v)
				vv.extended_args=vv.args
				vv.return_type=vv.cppname
				array.pushBack(luaclass.allMemberFunctions, vv)
			end

			-- post rectification
			for i,v in ipairs(luaclass.allMemberFunctions) do
				if #v.extended_args==1 and v.luaname=='__sub' then
					v.luaname='__unm' -- this has to be negation
					v.extended_args[2]=shallowCopyTable(v.extended_args[1])  
					v.extended_args[2].n='self2' -- lua_gettop is 2 for __unm operator. A bit unintuitive. 
				end

				if v.ifndef then
					v.if_='!defined ('..v.ifndef..')'
				elseif v.ifdef then
					v.if_='defined ('..v.ifdef..')'
				end
			end
			
			tagOverloadedFunctions(luaclass.allMemberFunctions)
		end
	end
	gen_lua.definitionDBbuilt=true
	gen_lua.bindTargetAll=bindTarget
end
function writeHeader(bindTarget)
	bindTarget = bindTarget or gen_lua.bindTargetAll
	local defname= 'genlua_'..string.gsub(input_filename, '%.','_')..tostring(Hash(input_filename))..'_def'..gen_lua.writeHeaderCounter
	gen_lua.writeHeaderCounter=gen_lua.writeHeaderCounter+1
	addLine('#ifndef '..defname)
	addLine('#define '..defname)
	addLine('// declare all classes before including this file')
	addLine('// e.g. class LMat; class LMatView; .... ')
	addLine('// The forward declaration is not included here because luna_gen cannot distinguish struct, class, or namespace.')

	if verbosecpp then
		addLine('// : number denotes the line number of luna_gen.lua which generated that line')
	end
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.decl then
			addLine(luaclass.decl)
		end
	end

	-- write declarations
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.if_ then
			addLine('#if '..luaclass.if_)
		end
		local cppinterface_name=luaclass.cppinterface_name
		if not luaclass.isModule then
			addLine('template<>')
		end
		addLine(' class '..cppinterface_name..' {\npublic:')
		local luaclass_name_o=luaclass.name
		local luaclass_name=luaclass.uniqueLuaClassname
		local cppclass_name=normalizedClassNameToClassName(luaclass.className)
		if luaclass.isModule then
			addLine('\tstatic const char moduleName[];')
			addLine('\ttypedef LunaModule<'..cppinterface_name.."> luna_t;")
			addLine('\tstatic luna_RegType methods[];')
		else
			addLine('\tstatic const char className[]; ')
			addLine('\tstatic const int uniqueID;  ')
			addLine('\tstatic luna_RegType methods[];')
			--addLine('\ttypedef Luna<'..normalizedClassNameToClassName(cppclass_name)..' > luna_t;')
			addLine('\tstatic '..cppclass_name..'* _bind_ctor(lua_State *L);')
			addLine('\tstatic void _bind_dtor('..cppclass_name..'* obj);')
			local cpp_parent_classname=normalizedClassNameToClassName(luaclass.uppermostParent.className)
			addLine('\ttypedef '..cpp_parent_classname..' base_t;')
			if luaclass.defineIndexMetaMethods then
				addLine('static luna__hashmap properties;')
				addLine('static luna__hashmap write_properties;')
			end
		end
		addLine('};')
		if luaclass.if_ then
			addLine('#endif //'..luaclass.if_)
		end
	end
	addLine('#endif')
	gen_lua.declarationWritten =true
end
function writeDefinitions(bindTarget, bindfunc_name)

	if gen_lua.definitionDBbuilt==false then
		print('warning! calling buildDefinitionsDB.')
		buildDefinitionsDB(bindTarget)
	end
	local ctn=gen_lua.cpp_contents
	if not gen_lua.declarationWritten then
		writeHeader()
	end
	-- write definitions
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.if_ then
			addLine('#if '..luaclass.if_)
		end
		local luaclass_name_o=luaclass.name
		local luaclass_name=luaclass.uniqueLuaClassname
		local cppclass_name=normalizedClassNameToClassName(luaclass.className)
		local cppinterface_name=luaclass.cppinterface_name

		if luaclass.globalWrapperCode then
			addLine(luaclass.globalWrapperCode)
		end

		if not luaclass.isModule then
			addLine('template<>')
		end

		addLine(' class impl_'..cppinterface_name..' {\npublic:')
		if luaclass.isModule then
			addLine('\ttypedef LunaModule<'..cppinterface_name.."> luna_t;")
		else
			addLine('\ttypedef Luna<'..normalizedClassNameToClassName(cppclass_name)..' > luna_t;')
		end
		if verbosecpp then
			addLine('// : number denotes the line number of luna_gen.lua that generated the sentence')
		end


		do -- write checkRoutines
			--ctors
			if luaclass.ctors then
				for i,v in ipairs(luaclass.ctors) do
					local vv=luaclass.ctors[i]
					addLine('  inline static bool _lg_typecheck_'..vv.luaname..'(lua_State *L)\n  {')
					addTypeCheck(vv.args);
				end
			end
			
			if luaclass.allMemberFunctions then
				-- static members
				for i,v in ipairs(luaclass.allMemberFunctions) do
					local vv=luaclass.allMemberFunctions[i]

					addLine('  inline static bool _lg_typecheck_'..vv.luaname..'(lua_State *L)\n  {')
					addTypeCheck(vv.extended_args);
				end
			end
		end

		if luaclass.ctors then -- write ctor (wrapper function body definition)
			for i,ctor in ipairs(luaclass.ctors) do

				local arg=ctor.args

				local def='static '..cppclass_name..'* _bind_'..ctor.luaname..'(lua_State *L)\n  {'
				addLine('  inline '..def)
				if gen_lua.use_profiler then addLine(' FractionTimer _lunagen_fractiontimer;') end

				addParsedArg(ctor, arg);

				addLine('	return new '..cppclass_name..'('..parsedNames(arg)..');')
				addLine('  }')
			end
			if luaclass.ctors.overloaded then
				addOverloadDefinition(cppclass_name..'*', 'NULL', luaclass.ctors.overloaded)
			end
		end

		if luaclass.wrapperCode then
			addLine(luaclass.wrapperCode)
		end

		do -- write members (wrapper function body definition)
			for i,v in ipairs(luaclass.allMemberFunctions) do
				local vv=luaclass.allMemberFunctions[i]
		
				addLine('  static int _bind_'..vv.luaname..'(lua_State *L)\n  {')
				if gen_lua.use_profiler then addLine(' FractionTimer _lunagen_fractiontimer;') end

				addParsedArg(vv, vv.extended_args)
				addReturnType(vv,vv.return_type)
			end
			if luaclass.allMemberFunctions.overloaded then
				addOverloadDefinition('int', '0', luaclass.allMemberFunctions.overloaded)
			end
		end

		local functionsToRegister={}
		local function collectDefinition(functions)
			if functions then
				for i,v in ipairs(functions) do
					local vv=functions[i]
					if not vv.overloaded then
						array.pushBack(functionsToRegister, {'_bind_'..vv.luaname, vv.luaname})
					end
				end
				if functions.overloaded then
					for k,v in pairs(functions.overloaded) do
						array.pushBack(functionsToRegister,{'_bind_'..k,k})
					end
				end
			end
		end
		collectDefinition(luaclass.allMemberFunctions)

		if luaclass.customFunctionsToRegister then
			for i,v in ipairs(luaclass.customFunctionsToRegister) do
				if type(v)=='table' then
					array.pushBack(functionsToRegister,{v[1],v[2]})
				else
					array.pushBack(functionsToRegister,{v,v})
				end
			end
		end

		if luaclass.defineInheritedIndexMetaMethods then
			local cppclass_name=normalizedClassNameToClassName(luaclass.className)
			if luaclass.defineIndexMetaMethods then
				addLine('  static void luna_init_hashmap()\n  {')
				for i,v in ipairs(luaclass.read_properties) do
					addLine('	LunaTraits<'..cppclass_name..' >::properties["'..v[1]..'"]=&_bind_'..v[2] ..';')
				end
				addLine('  }')
				addLine('  static void luna_init_write_hashmap()\n  {')
				if luaclass.write_properties then
					for i,v in ipairs(luaclass.write_properties) do
						addLine('	LunaTraits<'..cppclass_name..' >::write_properties["'..v[1]..'"]=&_bind_'..v[2] ..';')
					end
				end
				addLine('  }')
			end
			array.pushBack(functionsToRegister,{'__index','__index'})
			array.pushBack(functionsToRegister,{'__newindex', '__newindex'})
			addLine([[
			static int __index(lua_State* L)
			{ ]])
			do
				local lc=luaclass
				while lc do
					
					local cppclass_name=normalizedClassNameToClassName(lc.className)
					if lc.read_properties and #lc.read_properties>0 then
						addLine([[
						{
						luna__hashmap::iterator i=LunaTraits<]]..cppclass_name..[[ >::properties.find((const char*)lua_tostring(L,2));

						if (i!=LunaTraits<]]..cppclass_name..[[ >::properties.end())
						{
							luna_mfp fnc=i->second;
							lua_pop(L,1); // remove self
							return fnc(L); 
						}
							}
						]])
					end
					if lc.inheritsFrom then
						lc=findLunaClassProperty({c=lc.inheritsFrom})
					else
						lc=nil
					end
				end
			end

				if luaclass.isExtendableFromLua then
					addLine([[
					luna_t::userdataType* u=luna_t::checkRaw(L,1);
					if (u->has_env){
						lua_getfenv(L,1);
						lua_pushvalue(L,2);
						lua_gettable(L,-2);
						if( !lua_isnil(L,-1))
						return 1;
					}
					]])
				end
					addLine([[
					int mt=lua_getmetatable(L, 1);
					if(mt==0) luaL_error(L,"__index");//end
					lua_pushstring(L, lua_tostring(L,2));
					lua_rawget(L, -2);
					return 1;
					]])
				
				addLine([[
			}]])

			addLine([[ 
			static int __newindex(lua_State* L) {]])
			do
				local lc=luaclass
				while lc do
					
					local cppclass_name=normalizedClassNameToClassName(lc.className)
					if lc.write_properties and #lc.write_properties>0 then
						addLine([[
						luna__hashmap::iterator i=LunaTraits<]]..cppclass_name..[[ >::write_properties.find((const char*)lua_tostring(L,2));
						if (i!=LunaTraits<]]..cppclass_name..[[ >::write_properties.end())
						{
							luna_mfp fnc=i->second;
							lua_insert(L,2); // swap key and value
							lua_settop(L,2); // delete key
							return fnc(L); 
						}
						]])
					end
					if lc.inheritsFrom then
						lc=findLunaClassProperty({c=lc.inheritsFrom})
					else
						lc=nil
					end
				end
			end
				
				if luaclass.isExtendableFromLua then
					addLine([[
					luna_t::userdataType* u=luna_t::checkRaw(L,1);
					if(!u->has_env) {
						lua_newtable(L);
						lua_pushvalue(L,-1);
						lua_setfenv(L,1);
						u->has_env=1;
					} else lua_getfenv(L,1);
					lua_replace(L,1);
					lua_settable(L,1);
					return 0;
					]])

				else
					addLine([[
					luaL_error(L,"__newindex doesn't allow defining non-property member");
					return 0;
					]])
				end
				addLine([[
			}]])
		end

		addLine('}; // end of class impl_'..cppinterface_name)

		if luaclass.ctors then
			-- write ctor (wrapper function body definition)

			local def=cppclass_name..'* LunaTraits<'..cppclass_name..' >::_bind_ctor(lua_State *L)\n  {'
			addLine('  '..def)
			addLine('	return impl_LunaTraits<'..cppclass_name..' >::_bind_ctor(L);')
			addLine('  }')
			do -- write dtor
				addLine('  void LunaTraits<'..cppclass_name..' >::_bind_dtor('..cppclass_name..'* obj){')
				addLine('	delete obj;')
				addLine('  }')
			end
		elseif not luaclass.isModule then
			-- default ctor that does nothing
			local def=cppclass_name..'* LunaTraits<'..cppclass_name..' >::_bind_ctor(lua_State *L)\n  {'
			addLine('  '..def)
			addLine('   std::cerr<<"undefined contructor of '..cppclass_name..' called\\n";')
			addLine('	return NULL;')
			addLine('  }')
			addLine('  void LunaTraits<'..cppclass_name..' >::_bind_dtor('..cppclass_name..'* obj){')
			addLine('   delete obj;')
			addLine('  }')
		end

		
		if luaclass.isModule then
			addLine('const char '..cppinterface_name..'::moduleName[] = "'..luaclass_name..'";')
		else
			addLine('const char '..cppinterface_name..'::className[] = "'..luaclass_name..'";')
			addLine('const int '..cppinterface_name..'::uniqueID = '..luaclass.uniqueID..';')
		end
		if luaclass.defineIndexMetaMethods then
			addLine('luna__hashmap '..cppinterface_name..'::properties;')
			addLine('luna__hashmap '..cppinterface_name..'::write_properties;')
		end
		addLine('luna_RegType '..cppinterface_name..'::methods[] = {')
		for i,v in ipairs(functionsToRegister) do
			if string.sub(v[1],1,2)=="::" then
				addLine('	{"'..v[2]..'", &'..string.sub(v[1],3)..'},')
			else
				addLine('	{"'..v[2]..'", &impl_'..cppinterface_name..'::'..v[1]..'},')
			end
		end
		addLine('	{0,0}')
		addLine('};')

		if luaclass.if_ then
			addLine('#endif //'..luaclass.if_)
		end
	end

	addLine('void '..bindfunc_name..'(lua_State* L) {')
	addLine(codeDostring([[if __luna==nil then __luna={} end]]))
	addLine(codeDostring([[
	if __luna.copyMethodsFrom==nil then
		function __luna.copyMethodsFrom(methodsChild, methodsParent)
			for k,v in pairs(methodsParent) do
				if k~='__index' and k~='__newindex' and methodsChild[k]==nil then
					methodsChild[k]=v
				end
			end
		end
		function __luna.overwriteMethodsFrom(methodsChild, methodsParent)
			for k,v in pairs(methodsParent) do
				if k~='__index' and k~='__newindex' then
					if verbose then print('registering', k, methodsChild[k]) end
					methodsChild[k]=v
				end
			end
		end
	end
	]]))
	-- create namespaces
	local str=gen_lua.defNameSpaceCode;
	str=string.gsub(str,'\n', '\\n')
	str=string.gsub(str,'\t', ' ')
	if str~="" then
		addLine('	luna_dostring(L,"'..str..'");')
	end
	-- define registerFunction
	for iluaclass, luaclass in ipairs(bindTarget.classes) do
		if luaclass.if_ then
			addLine('#if '..luaclass.if_)
		end
		local luaclass_name=luaclass.name
		local cppclass_name=normalizedClassNameToClassName(luaclass.className)
		local cppinterface_name=luaclass.cppinterface_name
		if luaclass.defineIndexMetaMethods then
			addLine('   impl_'..cppinterface_name..'::luna_init_hashmap();')
			addLine('   impl_'..cppinterface_name..'::luna_init_write_hashmap();')
		end
		if luaclass.isModule then
			addLine('   LunaModule<'..cppinterface_name..' >::Register(L);')
		else
			addLine('	Luna<'..cppclass_name..' >::Register(L);')
		end
		if luaclass.namespace and luaclass.namespace~="" then
			if luaclass.namespace=="_G" and luaclass.isModule then
				addLine('	luna_dostring(L, "for k,v in pairs(__luna.'..luaclass.uniqueLuaClassname..') do _G[k]=v end");')
			else
				local n=luaclass.namespace
				addLine('	luna_dostring(L, "if not '..n..' then '..n..'={} end '..n..'.'..luaclass_name..'=__luna.'..luaclass.uniqueLuaClassname..'");')
				addLine(codeDostring([[
				__luna.]]..luaclass.uniqueLuaClassname..[[.luna_class=']]..string.sub(luaclass.namespace,4)..'.'..luaclass_name..[[']]))
			end
		else
			if luaclass.isModule then
				addLine(codeDostring([[ 
				if ]]..luaclass_name..[[==nil then 
					]]..luaclass_name..[[={}
				end 
				__luna.overwriteMethodsFrom(]]..	luaclass_name..[[, __luna.]].. luaclass.uniqueLuaClassname..[[)
				]]))
			else
				addLine('	luna_dostring(L, "'..luaclass_name..'=__luna.'..luaclass.uniqueLuaClassname..'");')
				addLine(codeDostring([[
				__luna.]]..luaclass.uniqueLuaClassname..[[.luna_class=']]..luaclass_name..[[']]))
			end	
		end
		if luaclass.inheritsFrom then
			local pc= findLunaClassProperty({c=luaclass.inheritsFrom })
			if pc.iluaclass and pc.iluaclass>luaclass.iluaclass then
				lgerror('Syntax error: bind definition of the parent class '..pc.name..' appeared later then inherited class '..luaclass.name)
			end
			addLine(codeDostring([[
			__luna.copyMethodsFrom(__luna.]]..luaclass.uniqueLuaClassname..[[, __luna.]]..pc.uniqueLuaClassname..[[)]]))
						   end
						   if luaclass.enums then
							   for k, v in ipairs(luaclass.enums) do
								   addLine('{\n  std::stringstream stringStreams;// defining enums')
								   if luaclass.namespace and luaclass.namespace~="" then
									   addLine('  stringStreams <<"'..luaclass.namespace..'.'..luaclass.name..'.'..v[1]..'="<< '..v[2]..';')
								   else
									   addLine('  stringStreams <<"'..luaclass.name..'.'..v[1]..'="<< '..v[2]..';')
								   end
--								   addLine('  std::string s=;')
								   addLine('  luna_dostring(L, stringStreams.str().c_str());\n}')
							   end
						   end
						   
		if luaclass.if_ then
			addLine('#endif //'..luaclass.if_)
		end
	end
	addLine('}')
end

do -- main
	if #arg>=1 then
		local input_file=arg[1]


		if not os.isFileExist(input_file) then
			print(input_file, 'not exist')
		else
			input_filename, input_filepath=os.processFileName(arg[1])
			package.path=input_filepath.."/?.lua;"..package.path
			dofile(input_file)
			-- set options
			if arg[2] then
				local succ, msg=pcall(loadstring(table.concat(arg,' ',2)))
				if not succ then print("Error! ", msg) end
			end
			generate() -- defined in the input file
		end
	else
		print('usage: lua luna_gen.lua input_file [options]')
		print(' e.g. lua luna_gen.lua a.lua verbosecpp=true') 
	end
end
